﻿using DataVirtualization.Toolkit;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;
using GalaSoft.MvvmLight.Views;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using Wimm.BusinessLogic.Services;
using Wimm.Common;
using Wimm.Common.Entities;
using Wimm.Common.Entities.Utils;
using Wimm.Common.Interfaces.Repositories;
using Wimm.Common.Interfaces.Services;
using Wimm.Common.Interfaces.Utils;
using Wimm.CustomContols;

namespace Wimm.UI.ViewModels
{
    public class MainHubViewModel: ViewModelBase
    {
        private PagedGridData<Expense, ExpenseIcon> _expensesLoader;
        private PagedGridData<Income, IncomeIcon> _incomesLoader;

        private IExpenseService _expensesService;
        private IExpenseRepository _expencesRepository;
        private IIconRepository _iconRepository;
        private IUserRepository _userRepository;
        private INavigationService _navigationService;
        private IIncomeRepository _incomeRepository;
        private IIncomeService _incomeService;
        private ILoginService _loginService;
        private IUserService _userService;

        private AddExpenseViewModel _addExpenseVm;
        private AddIncomeViewModel _addIncomeVm;
        private MultiLoginViewModel _multiLoginVM;
        private RegistrationViewModel _registrationViewModel;
        private YearDiagramViewModel _yearDiagramVM;

        private IEnumerable<KeyValuePair<ExpenseCategory, decimal>> _groupedExpenses;
        private IEnumerable<KeyValuePair<IncomeSource, decimal>> _groupedIncomes;
        
        public YearDiagramViewModel YearDiagramVM
        {
            get
            {
                if (_yearDiagramVM == null)
                { 
                    _yearDiagramVM = new YearDiagramViewModel(); 
                }

                return _yearDiagramVM;
            }
        }
        public MultiLoginViewModel MultiLoginVM
        {
            get
            {
                if (_multiLoginVM == null)
                    _multiLoginVM = new MultiLoginViewModel(_navigationService, _userService, _loginService);


                return _multiLoginVM;
            }
        }
        public RegistrationViewModel RegistrationVM
        {
            get
            {
                if (_registrationViewModel == null)
                    _registrationViewModel = new RegistrationViewModel(_navigationService, _userService);

                return _registrationViewModel;
            }
        }
        
        public AddIncomeViewModel AddIncomeViewModel
        {
            get
            {
                if (_addIncomeVm == null)
                    _addIncomeVm = new AddIncomeViewModel(_incomeService, _navigationService, LogedUser.Id, this.Language);

                return _addIncomeVm;
            }
        }
        public AddExpenseViewModel AddExpenseViewModel
        {
            get
            {
                if (_addExpenseVm == null)
                    _addExpenseVm = new AddExpenseViewModel(_expensesService, _navigationService, LogedUser.Id, this.Language);

                return _addExpenseVm;
            }
        }

        public User LogedUser { get; set; }
        public string Language { get; set; }

        #region Filters
        private FilterData _incomesFilterData;
        private FilterData _expensesFilterData;
        public FilterData ExpensesFilterData
        {
            get { return _expensesFilterData; }
            set { Set<FilterData>(() => ExpensesFilterData, ref _expensesFilterData, value); }
        }
        public FilterData IncomesFilterData
        {
            get { return _incomesFilterData; }
            set { Set<FilterData>(() => IncomesFilterData, ref _incomesFilterData, value); }
        }
        public DataItemCollection ExpensesDiagramSource
        {
            get 
            {
                DataItemCollection items = new DataItemCollection();
                if (_groupedExpenses != null)
                {
                foreach (KeyValuePair<ExpenseCategory, decimal> pair in _groupedExpenses)
                {
                    items.Add(new DataItem()
                    {
                            DataSymbol = "Сurrency".GetStringFromResource(this.Language),
                            ID = pair.Key.Id,
                            Name = pair.Key.Description.GetStringFromResource(this.Language),
                            Value = (double)pair.Value
                    });
                }
                }
                return items; 
            }
        }
        public DataItemCollection IncomesDiagramSource
        {
            get
            {
                DataItemCollection items = new DataItemCollection();
                if (_groupedIncomes != null)
                {
                    foreach (var pair in _groupedIncomes)
                    {
                        items.Add(new DataItem()
                        {
                            DataSymbol = "Сurrency".GetStringFromResource(this.Language),
                            ID = pair.Key.Id,
                            Name = pair.Key.Description.GetStringFromResource(this.Language),
                            Value = (double)pair.Value
                        });
        }
                }
                return items;
            }
        }
 
        private decimal _expenseTotal;
        private decimal _incomeTotal;
        public decimal IncomeTotal
        {
            get
            {
                return _incomeTotal;
            }
            set
            {
                Set<decimal>(() => IncomeTotal, ref _incomeTotal, value);
            }
        }
        public decimal ExpenseTotal
        {
            get
            {
                return _expenseTotal;
            }
            set
            {
                Set<decimal>(() => ExpenseTotal, ref _expenseTotal, value);
            }
        }

    	#endregion
        
        private MainHubViewModel()
        {
            this.Language = Windows.Globalization.ApplicationLanguages.Languages.First() ?? "en-US";
            this.ExpensesLoader = new PagedGridData<Expense, ExpenseIcon>(ExpenseRequest, true);
            this.IncomesLoader = new PagedGridData<Income, IncomeIcon>(IncomeRequest, true);

            ExpensesFilterData = new FilterData(DateTimeOffset.Now,
                DateTimeOffset.Now,
                TimePeriod.GetConfiguration(this.Language),
                this.Language,
                new RelayCommand(ApplyExpenseAction));
            IncomesFilterData = new FilterData(DateTimeOffset.Now,
                DateTimeOffset.Now,
                TimePeriod.GetConfiguration(this.Language),
                this.Language,
                new RelayCommand(ApplyIncomeAction));
        }

        private void ApplyIncomeAction()
        {
            Messenger.Default.Send<RefreshIncomeGrid>(new RefreshIncomeGrid());
        }
        private void ApplyExpenseAction()
        {
            Messenger.Default.Send<RefreshExpenseGrid>(new RefreshExpenseGrid());
        }

        public MainHubViewModel(IUserRepository userRepo, IExpenseRepository expensesRepo, IIconRepository iconRepo, INavigationService naviService, IIncomeRepository incomeRepository)
            : this()
        {
            _incomeRepository = incomeRepository;
            _iconRepository = iconRepo;
            _expencesRepository = expensesRepo;
            _userRepository = userRepo;
            _navigationService = naviService;
            _expensesService = new ExpenseService(_expencesRepository, _iconRepository);
            _incomeService = new IncomeService(_incomeRepository, _iconRepository);
            _loginService = new LoginService(_userRepository);
            _userService = new UserService(_userRepository);
        }

        public async Task InitModelDataAsync()
        {
            await _userRepository.SeedData();
            await _expencesRepository.SeedData();
            await _incomeRepository.SeedData();

            await AsyncGroupExpenses();
            await AsyncGroupIncomes();

            ExpensesFilterData.SetAllCategories(await _expensesService.LoadCategories(), this.Language, false);
            IncomesFilterData.SetAllCategories(await _incomeService.LoadCategories(), this.Language);
        }
        public async Task AsyncGroupExpenses()
        {
            if (this.LogedUser != null)
            {
                _groupedExpenses = await _expensesService.LoadExpenseGroups(
                    this._expensesFilterData.FromFilter.Date,
                    this._expensesFilterData.ToFilter.Date,
                    this.LogedUser.Id,
                    this._expensesFilterData.Detailed);
                decimal sum = 0;
                foreach (var item in _groupedExpenses)
                {
                    sum += item.Value;
                }
                this.ExpenseTotal = sum; 
            }
        }
        public async Task AsyncGroupIncomes()
        {
            if (this.LogedUser != null)
            {
                _groupedIncomes = await _incomeService.LoadIncomeGroups(
                    this._incomesFilterData.FromFilter.Date,
                    this._incomesFilterData.ToFilter.Date,
                    this.LogedUser.Id,
                    this._incomesFilterData.Detailed);
                decimal sum = 0;
                foreach (var item in _groupedIncomes)
                {
                    sum += item.Value;
                }
                this.IncomeTotal = sum;
            }
        }

        public void UpdateExpenseCategories()
        {
            ExpensesFilterData.SetOpacityCategories(_groupedExpenses.Select(x => x.Key).Select(y => y.Id));
        }
        public void UpdateIncomesCategories()
        {
            IncomesFilterData.SetOpacityCategories(_groupedIncomes.Select(x => x.Key).Select(y=>y.Id));
        }

        public async Task<String> DetectFirstApplicationPage()
        {
            return await _loginService.GetAuthenticationResult();
        }

        private async Task<IPagedResult<ExpenseIcon>> ExpenseRequest(IPagedRequest<Expense> expenseRequest)
        {
            ExpenseQuery query = new ExpenseQuery(expenseRequest, 
                this.LogedUser.Id,
                this.ExpensesFilterData.FromFilter, 
                this.ExpensesFilterData.ToFilter,
                this.ExpensesFilterData.SelectedCategory.Key);

            IPagedResult<Expense> expenses = await _expensesService.LoadExpenses(query);

            List<ExpenseIcon> iconsList = new List<ExpenseIcon>();

            foreach (Expense e in expenses.Data)
            {
                ExpenseCategory expenseCategory = await _expencesRepository.LoadCategoryById(e.ExpenseCategoryId);

                Icon icon = expenseCategory.IconId.HasValue ? await _iconRepository.GetById(expenseCategory.IconId.Value) : null;

                iconsList.Add(new ExpenseIcon(e, icon, expenseCategory.Name, _expensesService));
            }

            var expenseIconPagedResult = new GenericPagedResult<ExpenseIcon>(expenses.PageNumber, expenses.PageSize, expenses.TotalPages, iconsList);

            return expenseIconPagedResult;
        }

        private async Task<IPagedResult<IncomeIcon>> IncomeRequest(IPagedRequest<Income> incomeRequest)
        {
            var query = new IncomeQuery(incomeRequest,
                this.LogedUser.Id,
                this.IncomesFilterData.FromFilter, 
                this.IncomesFilterData.ToFilter,
                this.IncomesFilterData.SelectedCategory.Key);

            var incomes = await _incomeService.LoadPaged(query);

            var iconsList = new List<IncomeIcon>();

            foreach (Income e in incomes.Data)
            {
                var incomeSource = await _incomeRepository.LoadSourceById(e.IncomeSourceId);

                Icon icon = incomeSource.IconId.HasValue ? await _iconRepository.GetById(incomeSource.IconId.Value) : null;

                iconsList.Add(new IncomeIcon(e, icon, incomeSource.Name, _incomeService));
            }

            var incomeIconPagedResult = new GenericPagedResult<IncomeIcon>(incomes.PageNumber, incomes.PageSize, incomes.TotalPages, iconsList);

            return incomeIconPagedResult;
        }

        public PagedGridData<Expense, ExpenseIcon> ExpensesLoader
        {
            get
            {
                return _expensesLoader;
            }
            set
            {
                Set<PagedGridData<Expense, ExpenseIcon>>(() => ExpensesLoader, ref _expensesLoader, value);
            }
        }

        public PagedGridData<Income, IncomeIcon> IncomesLoader
        {
            get
            {
                return _incomesLoader;
            }
            set
            {
                Set<PagedGridData<Income, IncomeIcon>>(() => IncomesLoader, ref _incomesLoader, value);
            }
        }

        public async Task<List<FinancialStuff>> GetYearlyIncomeDiagramData()
        {
            List<FinancialStuff> result = new List<FinancialStuff>();
            DateTime currentMonth = new DateTime(DateTime.Now.Year, DateTime.Now.Month, 1);
            DateTime maxIncomeDate = await _incomeService.GetLimitDate(true);
            DateTime minIncomeDate = await _incomeService.GetLimitDate(false);
            minIncomeDate = new DateTime(minIncomeDate.Year, minIncomeDate.Month, 1);
            DateTime startDate = currentMonth.AddMonths(-12);
            if (currentMonth.AddMonths(-12) > minIncomeDate)
            {
                minIncomeDate = startDate;
            }

            for (var date = startDate; date < maxIncomeDate; date = date.AddMonths(1))
            {
                decimal amount = 0;
                if (date >= minIncomeDate)
                {
                    amount = (await _incomeService.LoadIncomeGroups(date, date.AddMonths(1).AddSeconds(-1), this.LogedUser.Id, false)).Sum(x => x.Value);
                }
                result.Add(new FinancialStuff(GetMonthName(date.Month), date.Year, (int)amount));
            }

            return result;
        }

        public async Task<List<FinancialStuff>> GetYearlyExpenseDiagramData()
        {
            List<FinancialStuff> result = new List<FinancialStuff>();
            DateTime currentMonth = new DateTime(DateTime.Now.Year, DateTime.Now.Month, 1);
            DateTime maxExpenseDate = await _expensesService.GetLimitDate(true);
            DateTime minExpenseDate = await _expensesService.GetLimitDate(false);
            minExpenseDate = new DateTime(minExpenseDate.Year, minExpenseDate.Month, 1);
            DateTime startDate = currentMonth.AddMonths(-12);
            if (currentMonth.AddMonths(-12) > minExpenseDate)
            {
                minExpenseDate = startDate;
            }

            for (var date = startDate; date < maxExpenseDate; date = date.AddMonths(1))
            {
                decimal amount = 0;
                if (date >= minExpenseDate)
                {
                    amount = (await _expensesService.LoadExpenseGroups(date, date.AddMonths(1).AddSeconds(-1), this.LogedUser.Id, false)).Sum(x => x.Value);
                }
                result.Add(new FinancialStuff(GetMonthName(date.Month), date.Year, (int)amount));
            }

            return result;
        }
       
        private string GetMonthName(int num)
        {
            switch (num)
            {
                case 1: return "MonthJanuary".GetStringFromResource(this.Language);
                case 2: return "MonthFebruary".GetStringFromResource(this.Language);
                case 3: return "MonthMarch".GetStringFromResource(this.Language);
                case 4: return "MonthApril".GetStringFromResource(this.Language);
                case 5: return "MonthMay".GetStringFromResource(this.Language);
                case 6: return "MonthJune".GetStringFromResource(this.Language);
                case 7: return "MonthJuly".GetStringFromResource(this.Language);
                case 8: return "MonthAugust".GetStringFromResource(this.Language);
                case 9: return "MonthSeptember".GetStringFromResource(this.Language);
                case 10: return "MonthOctober".GetStringFromResource(this.Language);
                case 11: return "MonthNovember".GetStringFromResource(this.Language);
                case 12: return "MonthDecember".GetStringFromResource(this.Language);
                default: return "";
            }
        }
    }
}
